package cn.ygh.bbo.common.utils;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.*;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;

/**
 * @author ygh
 * @Desc http工具包
 * @date 2019/11/3
 */
public class HttpUtils {

    private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);

    /**
     * 客户端
     */
    private static CloseableHttpClient httpClient=null;

    /**
     *最大超时时间
     */
    private static final int MAX_TIMEOUT = 7000;

    /**
     * 字节缓存数组大小
     */
    private static final int CACHE = 10 * 1024;

    /**
     * 连接池最大数量
     */
    private static final int MAX_TOTAL = 100;

    /**
     * 空闲永久连接检查间隔
     */
    private static final int VALIDATE = 1000;

    /**
     * 编码格式
     */
    private static final Charset charset=Charset.forName("Utf-8");

    /**
     * 客户端单例
     * @return
     */
    private static CloseableHttpClient getClient(){
        if (httpClient==null){
            synchronized (HttpUtils.class){
                if (httpClient==null){
                    // 设置连接池
                    PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager();
                    // 设置连接池大小
                    connMgr.setMaxTotal(MAX_TOTAL);
                    connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal() * 2);
                    connMgr.setValidateAfterInactivity(VALIDATE);
                    RequestConfig.Builder configBuilder = RequestConfig.custom();
                    // 设置连接超时
                    configBuilder.setConnectTimeout(MAX_TIMEOUT);
                    // 设置读取超时
                    configBuilder.setSocketTimeout(MAX_TIMEOUT);
                    // 设置从连接池获取连接实例的超时
                    configBuilder.setConnectionRequestTimeout(MAX_TIMEOUT);
                    RequestConfig requestConfig = configBuilder.build();
                    httpClient = HttpClients.custom()
                            .setSSLSocketFactory(createSSLConnSocketFactory())
                            .setDefaultRequestConfig(requestConfig).build();
                }
            }
        }
        return httpClient;
    }

    /**
     * 创建SSL安全连接
     *
     * @return
     */
    private static SSLConnectionSocketFactory createSSLConnSocketFactory() {
        SSLConnectionSocketFactory sslsf = null;
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null
                    , (TrustStrategy) (chain, authType) -> true).build();
            sslsf = new SSLConnectionSocketFactory(sslContext, (s, sslSession) -> true);
        } catch (GeneralSecurityException e) {
            logger.error(e.getLocalizedMessage());
        }
        return sslsf;
    }

    /**
     * 发送 GET 请求（HTTP），不带输入数据
     *
     * @param url
     * @return
     */
    public static String doGet(String url) {
        return doGet(url, new HashMap<>(), new HashMap<>());
    }

    /**
     * 发送 GET 请求（HTTP），K-V形式
     *
     * @param url
     * @param params
     * @return
     */
    public static String doGet(String url, Map<String, Object> params, Map<String, String> headmap) {
        String apiUrl = addParams(url, params);
        HttpGet httpGet = new HttpGet(apiUrl);
        addHeader(httpGet, headmap);
        HttpResponse response = null;
        try {
            response = getClient().execute(httpGet);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                logger.error("响应状态码:" + response.getStatusLine().getStatusCode());
                return null;
            }
            HttpEntity entity = response.getEntity();
            return IOUtils.toString(entity.getContent(), charset);
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            consumeResponse(response);
        }
        return null;
    }



    /**
     * 发送 POST 请求（HTTP），不带输入数据
     *
     * @param apiUrl
     * @return
     */
    public static String doPost(String apiUrl) {
        return doPost(apiUrl, new HashMap<>(), new HashMap<>());
    }

    /**
     * 发送 POST 请求（HTTP），K-V形式
     *
     * @param apiUrl API接口URL
     * @param params 参数map
     * @return
     */
    public static String doPost(String apiUrl, Map<String, Object> params, Map<String, String> headmap) {
        String httpStr = null;
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        addHeader(httpPost, headmap);
        try {
            List<NameValuePair> pairList = new ArrayList<>(params.size());
            for (Entry<String, Object> entry : params.entrySet()) {
                NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
                        .getValue().toString());
                pairList.add(pair);
            }
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, charset));
            response = getClient().execute(httpPost);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                logger.error("响应状态码:" + response.getStatusLine().getStatusCode());
                return null;
            }
            HttpEntity entity = response.getEntity();
            httpStr=IOUtils.toString(entity.getContent(), charset);
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            consumeResponse(response);
        }
        return httpStr;
    }

    /**
     * 发送 POST 请求（HTTP），JSON形式
     *
     * @param apiUrl
     * @param json   json对象
     * @return
     */
    public static String doPostJson(String apiUrl, Object json, Map<String, String> headmap) {
        String httpStr = null;
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        addHeader(httpPost, headmap);
        try {
            StringEntity stringEntity = new StringEntity(JsonUtils.toJSONString(json), charset);//解决中文乱码问题
            stringEntity.setContentEncoding("UTF-8");
            stringEntity.setContentType("application/json");
            httpPost.setEntity(stringEntity);
            response = getClient().execute(httpPost);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                logger.error("响应状态码:" + response.getStatusLine().getStatusCode());
                return null;
            }
            HttpEntity entity = response.getEntity();
            httpStr = IOUtils.toString(entity.getContent(), charset);
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            consumeResponse(response);
        }
        return httpStr;
    }

    /**
     * upload 上传文件
     *
     * @param apiUrl API接口URL
     * @param file   参数文件名
     * @param params 参数map
     * @return
     */
    public static String upload(String apiUrl, String file, Map<String, Object> params, Map<String, String> headmap) {
        String httpStr = null;
        HttpPost httpPost = new HttpPost(apiUrl);
        CloseableHttpResponse response = null;
        addHeader(httpPost, headmap);
        try {
            MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
            multipartEntityBuilder.addBinaryBody("file", new File(file));
            for (Entry<String, Object> entry : params.entrySet()) {
                multipartEntityBuilder.addTextBody(entry.getKey(), entry.getValue().toString());
            }
            httpPost.setEntity(multipartEntityBuilder.build());
            response = getClient().execute(httpPost);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                logger.error("响应状态码:" + response.getStatusLine().getStatusCode());
                return null;
            }
            HttpEntity entity = response.getEntity();
            httpStr = IOUtils.toString(entity.getContent(), charset);
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            consumeResponse(response);
        }
        return httpStr;
    }

    /**
     * upload 下载
     *
     * @param apiUrl  API接口URL
     * @param disFile 目标下载目录
     * @return
     */
    public static void download(String apiUrl, String disFile, Map<String, Object> params, Map<String, String> headmap) {
        HttpGet httpGet = new HttpGet(apiUrl);
        CloseableHttpResponse response = null;
        FileOutputStream fileOutputStream=null;
        try {
            response = getClient().execute(httpGet);
            if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                logger.error("响应状态码:" + response.getStatusLine().getStatusCode());
                return;
            }
            //文件名获取
            String fileName = getFileName(response, apiUrl);
            if (StringUtils.isNotBlank(disFile)) {
                File file = new File(disFile);
                if (!file.exists()) {
                    file.mkdir();
                }
            } else {
                disFile = "";
            }
            File file = new File(disFile + "/" + fileName);
            String ext = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length());
            StringBuilder oname = new StringBuilder(fileName.substring(0, (fileName.lastIndexOf(".") == -1 ? fileName.length() : fileName.lastIndexOf("."))));
            //文件存在
            while (file.exists()) {
                oname.append("_").append((new Date()).getTime());
                fileName = oname + (StringUtils.isBlank(ext) ? "" : "." + ext);
                file = new File(disFile + "/" + fileName);
            }
            logger.info("filename:" + fileName);
            fileOutputStream = new FileOutputStream(file);
            //保存
            HttpEntity entity = response.getEntity();
            InputStream content = entity.getContent();
            IOUtils.copy(content, fileOutputStream);
            //关闭连接
            fileOutputStream.close();
        } catch (IOException e) {
            logger.error(e.getMessage());
        } finally {
            IOUtils.closeQuietly(fileOutputStream);
            consumeResponse(response);
        }
    }

    /**
     * 下载的文件名获取
     *
     * @param response
     * @param url
     * @return
     * @throws UnsupportedEncodingException
     */
    private static String getFileName(CloseableHttpResponse response, String url) throws UnsupportedEncodingException {
        String fileName = "";
        Header header = response.getFirstHeader("Content-disposition");
        if (header != null) {
            HeaderElement[] headerElements = header.getElements();
            NameValuePair nameValuePair = headerElements[0].getParameterByName("filename");
            if (nameValuePair != null) {
                fileName = nameValuePair.getValue();
                fileName = URLDecoder.decode(fileName, "utf-8");
            }
        }
        if (StringUtils.isBlank(fileName)) {
            fileName = url.substring(url.lastIndexOf("/") + 1);
            fileName = fileName.substring(0, fileName.indexOf("?"));
        }
        if (StringUtils.isBlank(fileName)) {
            fileName = new Random().nextInt(10000) + System.currentTimeMillis() + "";
        }
        return fileName;
    }

    /**
     * 释放响应
     * @param response
     */
    private static void consumeResponse(HttpResponse response){
        if (response != null) {
            try {
                EntityUtils.consume(response.getEntity());
            } catch (IOException e) {
                logger.error(e.getMessage());
            }
        }
    }

    /**
     * 请求头添加
     *
     * @param httpMessage
     * @param headmap
     */
    private static void addHeader(HttpMessage httpMessage, Map<String, String> headmap) {
        //更多头
        if (headmap != null && headmap.size() > 0) {
            Iterator<Entry<String, String>> iter = headmap.entrySet().iterator();
            while (iter.hasNext()) {
                Entry<String, String> entry = iter.next();
                String key = entry.getKey();
                String value = entry.getValue();
                httpMessage.addHeader(key, value);
            }
        }
    }

    /**
     * get url地址构建 添加参数
     *
     * @param url
     * @param params
     * @return
     */
    private static String addParams(String url, Map<String, Object> params) {
        if (params == null) {
            return url;
        }
        StringBuffer param = new StringBuffer();
        int i = 0;
        for (String key : params.keySet()) {
            if (i == 0){
                param.append("?");
            } else{
                param.append("&");
            }
            param.append(key).append("=").append(params.get(key));
            i++;
        }
        url += param;
        return url;
    }
}
